package cmd

import (
	"bytes"
	"fmt"
	"gitee.com/ysfzf/zerox/cmd/common"
	"gitee.com/ysfzf/zerox/helper"
	"gorm.io/gorm"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/joho/godotenv"
	"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
	Use:   "zerox",
	Short: "go-zero开发辅助工具",
	Run: func(cmd *cobra.Command, args []string) {
		force, _ := cmd.Flags().GetBool("force")
		gennrateGitignore()
		generateTemplate()
		generateEnvFile(force)

	},
}

func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
	rootCmd.Flags().BoolP("force", "f", false, "Force overwrite of existing .env file")
}

func Getenv(key, defaulf string) string {
	v := os.Getenv(key)
	if v == "" {
		return defaulf
	}
	return v
}

func generateEnvFile(force bool) {
	if helper.IsFile(".env") && !force {
		return
	}

	host := input("请输入数据库连接地址(默认127.0.0.1)", "127.0.0.1")
	user := input("请输入数据库登录名(默认root)", "root")
	pwd := input("请输入数据库登录密码(默认123456)", "123456")
	dbname := input("请输入数据库名称", "test")
	redisHost := input("请输入Redis服务连接地址(默认127.0.0.1)", "127.0.0.1")

	file, err := os.Create(".env")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	secret := helper.Md5(time.Now().String(), "zerox", 1)
	str := `
DB_DSN=%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true&loc=Local
DB_TYPE=mysql
PORT=8080
REDIS_HOST=%s:6379
JWT_SECRET=%s
JWT_EXPIRE=86400
LOG_MODE=console
LOG_LEVEL=info
`
	_, _ = file.WriteString(fmt.Sprintf(str, user, pwd, host, dbname, redisHost, secret))
	fmt.Println("Generated .env file,Don't forget to execute go mod init ...")
	err = godotenv.Load(".env")
	if err != nil {
		fmt.Println(err)
		return
	}
	ask := input("是否现在生成gorm.yaml文件，输入Y表示生成(默认是N)", "n")
	if strings.ToUpper(ask) == "Y" {
		generateGormProfile(false)
	}

	ask = input("是否现在生成api.yaml文件，输入Y表示生成(默认是N)", "n")
	if strings.ToUpper(ask) == "Y" {
		generateApiProfile(false)
	}

	ask = input("是否现在生成proto.yaml文件，输入Y表示生成(默认是N)", "n")
	if strings.ToUpper(ask) == "Y" {
		generateProtoProfile(false)
	}

	genmod(dbname)
}

func genmod(dbname string) {
	if helper.IsFile("go.mod") {
		return
	}
	exe := exec.Command("go", "mod", "init", dbname)
	var out bytes.Buffer
	exe.Stdout = &out

	if err := exe.Run(); err != nil {
		log.Fatal(err)
	}
	fmt.Println(out.String())
}

func gennrateGitignore() {
	if helper.IsFile(".gitignore") {
		return
	}
	file, err := os.Create(".gitignore")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	str := `
*.exe
.idea
.env
.code
logs
`
	_, err = file.WriteString(str)
	fmt.Println(err)
}
func input(prompt, defaultValue string) string {
	var text string
	fmt.Print(prompt)
	_, _ = fmt.Scanln(&text)
	if text == "" {
		return defaultValue
	}
	return text
}

func conndb() (*gorm.DB, error) {
	return Conndb()
}

func generateTemplate() {
	_, err := exec.LookPath("goctl")
	if err != nil {
		log.Fatal("**error** goctl not found")
	}
	exe := exec.Command("goctl", "template", "init", "--home", "./template")
	var out bytes.Buffer
	exe.Stdout = &out

	if err := exe.Run(); err != nil {
		log.Fatal(err)
	}
	fmt.Println(out.String())
	apiHandler := `package {{.PkgName}}

import (
	"net/http"
    "gitee.com/ysfzf/zerox"
	{{if .HasRequest}}"github.com/zeromicro/go-zero/rest/httpx"{{end}}
	"github.com/zeromicro/go-zero/core/logx"
	"runtime/debug"
	"os"
	"errors"
	{{.ImportPackages}}
)

func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r := recover(); r != nil {
				appDebug := os.Getenv("APP_DEBUG")
				if appDebug == "true" {
					debug.PrintStack()
				} else {
					logx.ErrorStack(r)
				}
				zerox.Response(w, nil, zerox.NewErrorCode(500, errors.New("internal server error")))
			}
		}()
		{{if .HasRequest}}var req types.{{.RequestType}}
		if err := httpx.Parse(r, &req); err != nil {
			zerox.Response(w, nil, err)
			return
		}
		if err := zerox.ValidStruct(&req); err != nil {
			zerox.Response(w, nil, err)
			return
		}
		{{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx, r)
		{{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})
		{{if .HasResp}}zerox.Response(w, resp, err){{else}}zerox.Response(w, nil, err){{end}}
	}
}	
	`
	fpath := "template/api/handler.tpl"
	err = os.Remove(fpath)
	if err != nil {
		log.Println(err)
	}
	err = common.WriteFile(fpath, apiHandler)
	if err != nil {
		log.Fatal(err)
	}
	apiMain := `package main

import (
	"flag"
	"fmt"
	_ "github.com/joho/godotenv/autoload"
//	"gorm.io/driver/mysql"
//	"gorm.io/gorm"
	{{.importPackages}}
)

var configFile = flag.String("f", "etc/{{.serviceName}}.yaml", "the config file")

func main() {
	flag.Parse()
	//连接数据库
	//db, err := gorm.Open(mysql.Open(os.Getenv("DB_DSN")))
	//if err != nil {
	//	panic(err)
	//}
 
	// query.SetDefault(db)

	var c config.Config
	conf.MustLoad(*configFile, &c, conf.UseEnv())

	server := rest.MustNewServer(c.RestConf)
	defer server.Stop()

	ctx := svc.NewServiceContext(c)
	handler.RegisterHandlers(server, ctx)

	fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
	server.Start()
}
	`
	fpath = "template/api/main.tpl"
	err = os.Remove(fpath)
	if err != nil {
		log.Println(err)
	}
	err = common.WriteFile(fpath, apiMain)
	if err != nil {
		log.Fatal(err)
	}

	apilogic := `package {{.pkgName}}

import (
	{{.imports}}
	"net/http"
)

type {{.logic}} struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
	req    *http.Request
}

func New{{.logic}}(ctx context.Context, svcCtx *svc.ServiceContext, r *http.Request) *{{.logic}} {
	return &{{.logic}}{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
		req:    r,
	}
}

func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {
	// todo: add your logic here and delete this line

	{{.returnString}}
}
`
	fpath = "template/api/logic.tpl"
	err = os.Remove(fpath)
	if err != nil {
		log.Println(err)
	}
	err = common.WriteFile(fpath, apilogic)
	if err != nil {
		log.Fatal(err)
	}

	apiEtc := `Name: {{.serviceName}}
Host: {{.host}}
Port: ${PORT}
Redis:
  Host: ${REDIS_HOST}
  Pass: null
  Type: node
Auth:
  AccessSecret: ${JWT_SECRET}
  AccessExpire: ${JWT_EXPIRE}
Log:
  Mode: ${LOG_MODE}
  KeepDays: 7
  Level: ${LOG_LEVEL}

`
	fpath = "template/api/etc.tpl"
	err = os.Remove(fpath)
	if err != nil {
		log.Println(err)
	}
	err = common.WriteFile(fpath, apiEtc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Done.")
}
